package geocoding;

import graph.ALGOBuslineEdge;
import graph.ALGOEdge;
import graph.ALGOGraph;
import graph.ALGOPlacemarkNode;
import graph.AStarGraph;
import graph.PTAStarGraph;
import graph.SearchableGraph;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.Log4jFactory;
import org.xml.sax.SAXException;

import com.keithpower.gekmlib.KMLParser;
import com.keithpower.gekmlib.Kml;
import com.keithpower.gekmlib.Placemark;

public class BusNetReader {
	private double minLat = Double.MAX_VALUE;
	private double maxLat = Double.MIN_VALUE;
	private double minLng = Double.MAX_VALUE;
	private double maxLng = Double.MIN_VALUE;
	private Hashtable<Integer,ALGOPlacemarkNode> stops;
	private Hashtable<String, LinkedList<TripStop>> stopsSeq;
	private Hashtable<String, String> direction;
	private SearchableGraph map;
	private Log myLog = LogFactory.getLog(BusNetReader.class);
	
	public BusNetReader() {	
		stops = new Hashtable<Integer,ALGOPlacemarkNode>();
		stopsSeq = new Hashtable<String, LinkedList<TripStop>>();
		direction = new Hashtable<String, String>();
	}
	
	public ALGOGraph readNetFromFile(String stopsFile, String timetableFile, String linesFile) {
		 map = new PTAStarGraph();

		 readBusStops(stopsFile);
		 readStopSequences(timetableFile);
		 readDirections(linesFile);
		 		 
		 myLog.info("file reading complete");

		 Set<Entry<String, LinkedList<TripStop>>> es = stopsSeq.entrySet();
		 for (Entry<String, LinkedList<TripStop>> entry : es) {
			String [] lineCodes = entry.getKey().split(";");
			String trip = lineCodes[0];
			String line = lineCodes[1];

			processTrip(entry.getValue(), trip, line);
		}

		 /*
		processTrip(stopsSeq.get("1;601"), "1", "601");
		processTrip(stopsSeq.get("11;601"), "11", "601");
		*/
		return map;
	}
	
	private void readStopSequences(String filename) {
		try {
			BufferedReader b = new BufferedReader(new FileReader(filename));
			String line, passTime;
			
			while ((line = b.readLine()) != null) {
				String [] tmp = line.split(";");
				String passage = tmp[0];
				String busline = tmp[1];
				String key = passage + ";" + busline;
				Integer stopid = Integer.valueOf(tmp[2]);
				LinkedList<TripStop> seq;
				String [] passTimeComp;
				int t;
				
				passTime = tmp[4];
				passTimeComp = passTime.split(":");

				t = (Integer.parseInt(passTimeComp[0])*60 + Integer.parseInt(passTimeComp[1]))*60 + Integer.parseInt(passTimeComp[2]);
				
				if (stopsSeq.containsKey(key)) seq = stopsSeq.get(key);
				else {
					seq = new LinkedList<TripStop>();
					stopsSeq.put(key, seq);
				}
				
				seq.add(new TripStop(stopid, passTime, t));
			}
		}
		catch (IOException e) {
			System.out.println("readStopSequences: " + e);
		}
	}
	
	private void processTrip(LinkedList<TripStop> tripStops, String trip, String line) {
		ListIterator<TripStop> li = tripStops.listIterator();
		TripStop source, dest;
		Integer fromId, toId;
		String lineName = direction.get(trip + line);
		
		source = li.next();
		fromId = source.getStopId();
		
		while (li.hasNext()) {
			dest = li.next();
			toId = dest.getStopId();
			
			ALGOPlacemarkNode fromNode = stops.get(fromId);
			ALGOPlacemarkNode toNode = stops.get(toId);
			ALGOBuslineEdge e = null;
			int i = 0;
			ALGOEdge [] linesFromHere = map.getEdgesForNode(fromNode.getNodeID());
			if (linesFromHere != null) {
				for (i = 0; i < linesFromHere.length; i++) {
					e = (ALGOBuslineEdge)linesFromHere[i];
					if ((e.getBusline() != null) && e.getBusline().equals(lineName)) break;
					else if (e.getBusline() == null) System.out.println("no busline given for + (" + trip + "," + line + "," + fromNode.getValue().getName() + "," + toNode.getValue().getName());
				}
			}
			
			if ((e == null) || (i == linesFromHere.length)) {
				int diff = dest.getT() - source.getT();
				map.addEdge(e = new ALGOBuslineEdge(fromNode.getNodeID(),
												toNode.getNodeID(),
												diff == 0 ? 1 : diff,
												lineName));
				source.setT(dest.getT() - source.getT());
				e.addTrip(Integer.valueOf(trip), source);
			}
			else { // there is already an edge for this line
				source.setT(dest.getT() - source.getT());
				e.addTrip(Integer.valueOf(trip), source);
			}
			
			fromId = toId;
			source = dest;
		}
	}
	
	private void readDirections(String filename) {
		try {
			BufferedReader b = new BufferedReader(new FileReader(filename));
			String line;
			
			while ((line = b.readLine()) != null) {
				String [] tmp = line.split(";");
				String lineKey = tmp[0] + tmp[2];
				String lineName = tmp[3];
				String endStop = tmp[5];
				
				if (!direction.containsKey(lineKey)) direction.put(lineKey, lineName + "-" + tmp[7] + " (Richtung " + endStop + ")"); 
			}
		}
		catch (IOException e) {
			
		}
	}
	
	private void readBusStops(String filename) {
		try {
			int nodeId = 0;
        	Kml kmlRoot = new KMLParser().parse(new InputStreamReader(new FileInputStream(filename)));
        	List<Placemark> pmList = kmlRoot.getDocument().getPlacemarks();
        	Integer id;
        	
        	for (Placemark placemark : pmList) {
        		String [] tmp = placemark.getName().split(":");
        		id = Integer.parseInt(tmp[0]);
        		if (!stops.containsKey(id)) {
        			ALGOPlacemarkNode pmn = new ALGOPlacemarkNode(nodeId ++, placemark);
        			stops.put(id, pmn);
        			map.addNode(pmn);
        		}
			}
		}
		catch (IOException e) {
			System.out.println(e);
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public double getMinLat() {
		return minLat;
	}
	
	public double getMaxLat() {
		return maxLat;
	}
	
	public double getMinLng() {
		return minLng;
	}
	
	public double getMaxLng() {
		return maxLng;
	}
}